package multi_thread.master.tools;

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

/**
 * @author Cheng Jun
 * Description: semaphore /ˈseməfɔː(r)/  信号量
 * semaphore 维护一组许可证, 每次尝试获取许可证时都将阻塞等待直到可获取，它才能获取到并解除阻塞状态。
 * 可以控制一些物理或逻辑资源的访问或使用，它常常用于限制线程数目。
 * 在实际开发中，可用作流量控制，特别对于一些公共资源有限的应用场景，如数据库连接，或是一些其他限流的缓存池
 * https://www.cnblogs.com/gocode/p/analysis-source-code-of-Semaphore.html
 *
 * 必须确保线程获得了许可证，才能去 release, 不然release 会导致许可证加一。
 * 多线程情况下，控制台中语句的输出顺序 不代表 程序在CPU中执行的先后顺序。
 * @version 1.0
 * @date 2020/3/7 14:23
 * @see Semaphore /ˈseməfɔː(r)/
 */
public class SemaphoreExample {

    public static void main(String[] args) {
        // 信号量中有一个 fair 字段，表示是否公平竞争，公平的话，先 acquire 的线程就会先拿到许可证，但是会影响一点并发性能
        Semaphore semaphore = new Semaphore(2, true);
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "已经运行");
                boolean acquireFlag = false;
                try {
                    // 注意信号量 release 一次，许可证就会增加一个；acquire 一次，许可证就会减少一个。
                    // 即使初始化信号量为 0，release 一次，许可证也会增加一个。
                    // semaphore.release();
                    acquireFlag = semaphore.tryAcquire(3, TimeUnit.SECONDS);
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println(Thread.currentThread().getName() + "继续运行");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    // 切记：要在finally 里面 释放许可证。不能写在try里面，如果try发生异常，信号量会释放不掉;
                    // 必须确保线程获得了许可证，才能去 release, 不然release 会导致许可证加一。
                    if (acquireFlag) {
                        semaphore.release();
                    }
                }
            }, "线程-" + i).start();
        }

    }
}
